/************************************************************
   Copyright 2008 Abhinav Pobbati

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*************************************************************/

#include "HkPrerequisites.h"

#include "Havok4Xna/Common/Base/Memory/HkMemory.h"

namespace Havok4Xna {
	void HkMemory::AllocateChunk(int nbytes, HkMemoryClass cl) {
		_this()->allocateChunk(nbytes, (HK_MEMORY_CLASS) cl);
	}
	void HkMemory::DeallocateChunk(IntPtr data, int nbytes, HkMemoryClass cl) {
		_this()->deallocateChunk((void*)data, nbytes, (HK_MEMORY_CLASS) cl);
	}
	IntPtr HkMemory::AllocateRuntimeBlock(int nbytes, HkMemoryClass cl) {
		return IntPtr(_this()->allocateRuntimeBlock(nbytes, (HK_MEMORY_CLASS) cl));
	}
	void HkMemory::DeallocateRuntimeBlock(IntPtr data, int nbytes, HkMemoryClass cl) {
		_this()->deallocateRuntimeBlock((void*)data, nbytes, (HK_MEMORY_CLASS) cl);
	}
	void HkMemory::PreAllocateRuntimeBlock(int nbytes, HkMemoryClass cl) {
		_this()->preAllocateRuntimeBlock(nbytes, (HK_MEMORY_CLASS) cl);
	}
	void HkMemory::ProvideRuntimeBlock(IntPtr data, int nbytes, HkMemoryClass cl) {
		_this()->provideRuntimeBlock((void*)data, nbytes, (HK_MEMORY_CLASS) cl);
	}
	void HkMemory::FreeRuntimeBlocks() {
		_this()->freeRuntimeBlocks();
	}
	void HkMemory::ReservePagesFromSystemMemory(int numBytes) {
		_this()->reservePagesFromSystemMemory(numBytes);
	}
	void HkMemory::GarbageCollect() {
		_this()->garbageCollect();
	}
	void HkMemory::Optimize() {
		_this()->optimize();
	}
	void HkMemory::ReleaseUnusedPagesToSystemMemory() {
		_this()->releaseUnusedPagesToSystemMemory();
	}
	int HkMemory::GetAllocatedSize(int size) {
		return _this()->getAllocatedSize(size);
	}
	bool HkMemory::IsDebugMemory() {
		return _this()->isDebugMemory();
	}
	void HkMemory::SetMemoryHardLimit(UInt32 maxMemory) {
		_this()->setMemoryHardLimit(maxMemory);
	}
	UInt32 HkMemory::GetMemoryHardLimit() {
		return _this()->getMemoryHardLimit();
	}
	void HkMemory::SetMemorySoftLimit(UInt32 maxMemory) {
		_this()->setMemorySoftLimit(maxMemory);
	}
	UInt32 HkMemory::GetMemorySoftLimit() {
		return _this()->getMemorySoftLimit();
	}
	bool HkMemory::HasMemoryAvailable(UInt32 size) {
		return _this()->hasMemoryAvailable(size);
	}
	bool HkMemory::IsOk() {
		return _this()->isOk();
	}
	IntPtr HkMemory::FindBaseAddress(IntPtr p, int nbytes) {
		return IntPtr(_this()->findBaseAddress((void*)p, nbytes));
	}
	void HkMemory::LockBlock(IntPtr p) {
		_this()->lockBlock((void*)p);
	}
	void HkMemory::UnlockBlock(IntPtr p) {
		_this()->unlockBlock((void*)p);
	}
	static void _HkMemory_WalkMemory(void* start, hk_size_t size, hkBool allocated, int pool, void* param) {
		if(HkMemory::memoryWalkCallback != nullptr)
			HkMemory::memoryWalkCallback(IntPtr(start), size, allocated, pool, IntPtr(param));
	}
	HkResult HkMemory::WalkMemory(HkMemoryWalkCallback ^callback, IntPtr param) {
		memoryWalkCallback = callback;
		HkResult result = (HkResult) _this()->walkMemory(_HkMemory_WalkMemory, (void*)param);
		memoryWalkCallback = nullptr;
		return result;
	}


	HkThreadMemory ^HkThreadMemory::GetInstance() {
		if(Instance == nullptr) 
			Instance = gcnew HkThreadMemory(nullptr, false);
		Instance->_setThis(&hkThreadMemory::getInstance(), false);
		return Instance;
	}
	HkThreadMemory::HkThreadMemory(HkMemory ^mainMemoryManager) {
		_setThis(new hkThreadMemory(mainMemoryManager->_this(), 16), true);
	}
	HkThreadMemory::HkThreadMemory(HkMemory ^mainMemoryManager, int maxNumElemsOnFreeList) {
		_setThis(new hkThreadMemory(mainMemoryManager->_this(), maxNumElemsOnFreeList), true);
	}
	IntPtr HkThreadMemory::Allocate(int nbytes, HkMemoryClass cl) {
		return IntPtr(_this()->allocate(nbytes, (HK_MEMORY_CLASS) cl));
	}
	void HkThreadMemory::Deallocate(IntPtr data) {
		_this()->deallocate((void*) data);
	}
	IntPtr HkThreadMemory::AllocateChunkConstSize(int nbytes, HkMemoryClass cl) {
		return IntPtr(_this()->allocateChunkConstSize(nbytes, (HK_MEMORY_CLASS) cl));
	}
	void HkThreadMemory::DeallocateChunkConstSize(IntPtr data, int nbytes, HkMemoryClass cl) {
		_this()->deallocateChunk((void*) data, nbytes, (HK_MEMORY_CLASS) cl);
	}
	IntPtr HkThreadMemory::AllocateStack(int nbytes) {
		return IntPtr(_this()->allocateStack(nbytes));
	}
	void HkThreadMemory::ShrinkAllocatedStack(IntPtr data, int newSize) {
		_this()->shrinkAllocatedStack((void*) data, newSize);
	}
	void HkThreadMemory::DeallocateStack(IntPtr data) {
		_this()->deallocateStack((void*) data);
	}
	IntPtr HkThreadMemory::AlignedAllocate(int alignment, int nbytes, HkMemoryClass cl) {
		return IntPtr(_this()->alignedAllocate(alignment, nbytes, (HK_MEMORY_CLASS) cl));
	}
	void HkThreadMemory::AlignedDeallcate(IntPtr data) {
		_this()->alignedDeallocate((void*) data);
	}
	void HkThreadMemory::SetStackArea(IntPtr buf, int nbytes) {
		_this()->setStackArea((void*) buf, nbytes);
	}
	void HkThreadMemory::AddReference() {
		_this()->addReference();
	}
	void HkThreadMemory::RemoveReference() {
		_this()->removeReference();
	}
	void HkThreadMemory::ReleaseCachedMemory() {
		_this()->releaseCachedMemory();
	}
	int HkThreadMemory::GetRow(int nbytes) {
		return _this()->getRow(nbytes);
	}
	int HkThreadMemory::RowToSize(int row) {
		return _this()->rowToSize(row);
	}
	

	HkDebugMemory::HkDebugMemory()
		: HkMemory(hkDebugMemory::create(), true) {
	}


	HkMallocMemory::HkMallocMemory()
		: HkMemory(new hkMallocMemory(), true) {
	}


	HkPoolMemory::HkPoolMemory()
		: HkMemory(new hkPoolMemory(), true) {
	}


	bool HkMemoryBlockServer::IsSingleBlockServer() {
		return _this()->isSingleBlockServer();
	}
	UInt32 HkMemoryBlockServer::RecommendSize(UInt32 size) {
		return _this()->recommendSize(size);
	}
	IntPtr HkMemoryBlockServer::Allocate(UInt32 size, [Out] UInt32 sizeOut) {
		return IntPtr(_this()->allocate(size, sizeOut));
	}
	void HkMemoryBlockServer::Free(IntPtr data, UInt32 size) {
		_this()->free((void*) data, size);
	}
	bool HkMemoryBlockServer::Resize(IntPtr data, UInt32 oldSize, UInt32 newSize, [Out] UInt32 sizeOut) {
		return _this()->resize((void*) data, oldSize, newSize, sizeOut);
	}
	UInt32 HkMemoryBlockServer::GetMinAllocSize() {
		return _this()->getMinAllocSize();
	}
	UInt32 HkMemoryBlockServer::GetTotalAvailableMemory() {
		return _this()->getTotalAvailableMemory();
	}
	UInt32 HkMemoryBlockServer::GetMemoryLimit() {
		return _this()->getMemoryLimit();
	}
	bool HkMemoryBlockServer::SetMemoryLimit(UInt32 size) {
		return _this()->setMemoryLimit(size);
	}
	IntPtr HkMemoryBlockServer::CriticalAlloc(UInt32 size) {
		return IntPtr(_this()->criticalAlloc(size));
	}
	void HkMemoryBlockServer::CriticalFree(IntPtr ptr, UInt32 size) {
		_this()->criticalFree((void*) ptr, size);
	}


	IntPtr HkFreeListMemoryServer::Alloc(UInt32 size) {
		return IntPtr(_this()->alloc(size));
	}
	void HkFreeListMemoryServer::Free(IntPtr data) {
		_this()->free((void*) data);
	}
	bool HkFreeListMemoryServer::IsValidAlloc(IntPtr data) {
		return _this()->isValidAlloc((void*) data);
	}
	UInt32 HkFreeListMemoryServer::GetAllocSize(IntPtr p, UInt32 size) {
		return _this()->getAllocSize((void*) p, size);
	}
	UInt32 HkFreeListMemoryServer::GetAllocTotalSize(IntPtr p, UInt32 size) {
		return _this()->getAllocTotalSize((void*) p, size);
	}
	IntPtr HkFreeListMemoryServer::CriticalAlloc(UInt32 size) {
		return IntPtr(_this()->criticalAlloc(size));
	}
	void HkFreeListMemoryServer::CriticalFree(IntPtr ptr, UInt32 size) {
		_this()->criticalFree((void*) ptr, size);
	}


	HkDefaultFreeListMemoryServer ^HkDefaultFreeListMemoryServer::GetInstance() {
		if(Instance == nullptr)
			Instance = gcnew HkDefaultFreeListMemoryServer(nullptr, false);
		Instance->_setThis(&hkDefaultFreeListMemoryServer::getInstance(), false);
		return Instance;
	}


	HkFreeList::HkFreeList(UInt32 elementSize, UInt32 alignment, UInt32 blockSize, HkFreeListMemoryServer ^alloc) {
		_setThis(new hkFreeList(elementSize, alignment, blockSize, alloc->_this()), true);
	}
	IntPtr HkFreeList::Alloc() {
		return IntPtr(_this()->alloc());
	}
	void HkFreeList::Free(IntPtr data) {
		_this()->free((void*) data);
	}
	void HkFreeList::FreeAllMemory() {
		_this()->freeAllMemory();
	}
	void HkFreeList::FreeAll() {
		_this()->freeAll();
	}
	UInt32 HkFreeList::GetElementSize() {
		return _this()->getElementSize();
	}
	bool HkFreeList::IsOk() {
		return _this()->isOk();
	}
	bool HkFreeList::IsFreeElementAvailable() {
		return _this()->isFreeElementAvailable();
	}
	int HkFreeList::FindGarbage() {
		return _this()->findGarbage();
	}
	void HkFreeList::GarbageCollect() {
		_this()->garbageCollect();
	}
	int HkFreeList::FreeAllFreeBlocks() {
		return _this()->freeAllFreeBlocks();
	}
	bool HkFreeList::HasFreeBlocks() {
		return _this()->hasFreeBlocks();
	}
	void HkFreeList::FreeSmallFreeBlocks(UInt32 minSize) {
		_this()->freeSmallFreeBlocks(minSize);
	}
	UInt32 HkFreeList::GetNumFreeElements() {
		return _this()->getNumFreeElements();
	}
	UInt32 HkFreeList::GetTotalNumElements() {
		return _this()->getTotalNumElements();
	}
	UInt32 HkFreeList::GetNumUsedElements() {
		return _this()->getNumUsedElements();
	}
	UInt32 HkFreeList::GetBlockSize() {
		return _this()->getBlockSize();
	}
	int HkFreeList::GetTotalNumBlocks() {
		return _this()->getTotalNumBlocks();
	}
	static void _FreeList_WalkMemory(void* start, hk_size_t size, hkBool allocated, int pool, void* param) {
		if(HkMemory::memoryWalkCallback != nullptr)
			HkMemory::memoryWalkCallback(IntPtr(start), size, allocated, pool, IntPtr(param));
	}
	void HkFreeList::WalkMemory(HkMemoryWalkCallback ^callback, int pool, IntPtr param) {
		memoryWalkCallback = callback;
		_this()->walkMemory(_FreeList_WalkMemory, pool, param.ToPointer());
		memoryWalkCallback = nullptr;
	}


	HkFixedMemoryBlockServer::HkFixedMemoryBlockServer(IntPtr start, UInt32 size)
		: HkMemoryBlockServer(new hkFixedMemoryBlockServer((void*) start, size), true) {
	}

	HkSystemMemoryBlockServer::HkSystemMemoryBlockServer()
		: HkMemoryBlockServer(new hkSystemMemoryBlockServer(), true) {
	}
	HkSystemMemoryBlockServer::HkSystemMemoryBlockServer(UInt32 minBlockSize)
		: HkMemoryBlockServer(new hkSystemMemoryBlockServer(minBlockSize), true) {
	}
}
